/* bootloader 最终目的是加载内核
* 在这个过程中要完成一些初始化，为内核启动做准备,
* 比如内存,kernel的读取,而kernel的读取要初始化nand,
* 如果要更快的执行，就要提高时钟，故要做以下初始化:
* 1.关看门狗
* 2.初始化时钟
* 3.初始化UART
* 4.初始化SDRM
* 5.初始化NAND
* 6.代码重定位
* 7.清除bss段
* 在进入kernel之前，还要提供一些必要的信息给kernel，就要做tag
*/
#define BWSCON 0x48000000
.text
.global _start
_start:
	bl close_watch_dog
	bl set_clock

/* 一、 关闭看门狗 */
close_watch_dog:
	ldr r0, =0x53000000
	ldr r1, =0
	str r1, [r0]
	mov pc, lr

/* 二、配置时钟
		1.LOCKTIME 0x4C000000 = 0xFFFFFFFF
		2.CLKCON 0x4C00000C = 0xFFFFF0 (默认，先不配置，如果要低功耗，就要选择性关闭)
			Mpll = (2 * m * Fin) / (p * 2S)
			m = (MDIV + 8), p = (PDIV + 2), s = SDIV
		3.CLKDIVN 0x4C000014
		DIVN_UPLL	0	: UCLK = UPLL clock
		HDIVN		10	: HCLK = FCLK/4 when CAMDIVN[9] = 0
		PDIVN		1	: PCLK has the clock same as the HCLK/2
		4.协处理器
		MMU_SetAsyncBusMode
			mrc p15,0,r0,c1,c0,0
			orr r0,r0,#0xc0000000					#R1_nF:OR:R1_iA = 0xc0000000
			mcr p15,0,r0,c1,c0,0

		5.PLL的MPS
		UPLLCON 0x4C000008 = (56<<12) | (2<<4) | (2<<0)
		7个nop When you set MPLL&UPLL values,
			you have to set the UPLL value first and then the MPLL value. (Needs intervals approximately 7 NOP)
		MPLLCON 0x4C000004 = (127<<12)| (2<<4) | (1<<0)
	*/
	/* 1.LOCKTIME 0x4C000000 = 0xFFFFFFFF */
set_clock:
	ldr r0, = 0x4C000000
	ldr r1, = 0xFFFFFFFF
	str r1, [r0]

	// 3.CLKDIVN 0x4C000014
	ldr r0, = 0x4C000014
	ldr r1, = ((0<<3) |(2<<1) | (1<<0))
	str r1, [r0]

	/* 4.协处理器 ICACHE*/
	mrc p15,0,r0,c1,c0,0
	orr r0,r0,#0xc0000000
	mcr p15,0,r0,c1,c0,0


	/* 5.PLL的MPS	UPLLCON 0x4C000008 = (56<<12) | (2<<4) | (2<<0) */
	ldr r0, = 0x4C000008
	ldr r1, = (56<<12) | (2<<4) | (2<<0)
	str r1, [r0]

	nop
	nop
	nop
	nop
	nop
	nop
	nop

	//	5.PLL的MPS	MPLLCON 0x4C000004 = (127<<12)| (2<<4) | (1<<0)
	ldr r0, = 0x4C000004
	ldr r1, = ((127<<12)| (2<<4) | (1<<0))
	str r1, [r0]

	/* 一旦设定 PLL, cpu就会等待，直到新的时钟产生，并以新的时钟工作*/

	/* 设置内存: sp 栈
	 * 根据片内ram可读可写，nor可读不可写的特性，
	 * 往0地址写入0，然后再读出来，比较0与写入后0地址的值是否一致，即可判断
	 */
	mov r0, #0
	ldr r1, [r0] /* 保存0地址原有的值 */
	str r0, [r0] /* 往0地址写入0 */
	ldr r2, [r0] /* 读取0地址的值，存入r2 */
	cmp r0, r2   /* 比较写入值r0与读取值r2是否相等 */
	ldr sp, =0x40000000+4096  /* 先设定为nor启动，如果读取值是0的话，后面就修改 */
	//ldr sp, =4096
	moveq sp, #4096	/*如果相等，设置sp栈地址 为nand启动*/
	streq r1, [r0]	/*如果相等，写回原来的值*/
	mov pc, lr

/* 三、初始化内存 SDRAM_INIT */
init_sdram:
	adr r0, SDRAM_CONFIG	//不可以用ldr r0, =SDRAM_CONFIG，有问题，详细请看ldr与adr例子			/*也可以使用adr r0, SDRAM_CONFIG*/
	ldr r1, =BWSCON
	add r2, r1, #(13*4)	/*mov r2, #(BWSCON + (13*4))*/

0:
	ldr r3, [r0], #4	/*取值后+4*/
	str r3, [r1], #4	/*赋值后+4*/
	cmp r1, r2
	bne 0b			/*b表示back，就是往回去的地方循环，f表示front，表示往前循环*/

__egtest:
	//ldr r0, SDRAM_CONFIG	//ldr	r0, [pc, #24]	; 33f800c0 <SDRAM_CONFIG>-----把pc+24这个地址的内容给r0，然后跳过去执行
	//adr r0, SDRAM_CONFIG	//汇编代码：add	r0, pc, #20	; 0x14------相对偏移，只是把pc偏移这个地址值，后面的ldr去取值
	//ldr r0, =SDRAM_CONFIG	//ldr	r0, [pc, #84]	; 33f80104 <SDRAM_CONFIG+0x44>
__egtestend:

	//bl sdram_init
// 在SDRAM初始化OK之后，顺便把堆栈也放在SDRAM
	ldr sp, =0x34000000

/*
* 四、代码重定位（拷贝代码到SDRAM）、清除BSS（清0），
* 	  在这之前，先判断是nor还是nand，
*	  如果是nand必须先初始化nand，然后才能从nand中读取数据
*/

/*
//第1种：汇编完成
	mov r0, #0				//代码存放的起始位置
	ldr r1, =__code_start		//代码运行的地址，就是lds中的0x33f80000
	ldr r2, =__bss_start	//到bss_start这里就是除bss段外所有的代码

copy_loop:

	ldr r3, [r0]	//从nor、nand读出
	str r3, [r1]	//写到SDRAM
	add r0, r0, #4	//每次完成后偏移
	add r1, r1, #4
	cmp r1, r2
	ble copy_loop

	ldr r0, =__bss_start
	ldr r1, =__bss_end
	mov r2, #0

clear_bss:
	str r2, [r0]
	add r0, r0, #4
	cmp r0, r1
	ble clear_bss

//第2种：利用r0,r1,r2是用来传递参数，传递--源--目的--长度
	//copy2sdram
	mov r0, #0				//代码存放的起始位置
	ldr r1, =__code_start		//代码运行的地址，就是lds中的0x33f80000
	ldr r2, =__bss_start	//到bss_start这里就是除bss段外所有的代码
	sub r2, r2, r1

	//clear_bss
	ldr r0, =__bss_start
	ldr r1, =__bss_end
	sub r1, r1, r0

//第3种：全部用c完成，在c函数中，使用从lds得到的参数
*///使用c实现代码拷贝，从lds中获得参数：源、目的、长度
	bl copycode2sdram
	bl clear_bss

/* 五、调到内存(SDRAM)去执行 */
	/* 调用main */ // bl main 是相对跳转，程序仍然在nor、sram(nand) 中执行
	//ldr lr, =halt
	ldr pc, =main	//使用绝对跳转，跳到SDRAM中执行
	//bl main

halt:
	b halt

SDRAM_CONFIG:
	.word 0x22000000	//BWSCON	目前只设置BANK6-7，其他的等后面在配置
	.word 0x00000700	//BANKCON0	BANKCON0-5使用默认值
	.word 0x00000700	//BANKCON1
	.word 0x00000700	//BANKCON2
	.word 0x00000700	//BANKCON3
	.word 0x00000700	//BANKCON4
	.word 0x00000700	//BANKCON5
	.word 0x00018001	//BANKCON6	查内存手册RAS to CAS Delay tRCD 15-18-20-ns，得到15-20ns，1个clock=100MHz等价于10ns
	.word 0x00018001	//BANKCON7	取20ns，就是2个clock，00 = 2 clocks 01 = 3 clocks 10 = 4 clocks
	.word 0x008404F5	//REFRESH	//
	.word 0x000000B1	//BANKSIZE	2表示128M SDRM 故这里也改为1，并禁用了burst模式，不然就是0xb1
	.word 0x00000020	//MRSRB6	0x20表示2个clock，0x30表示3个clock，其实这两个都可以
	.word 0x00000020	//MRSRB7	0x20表示2个clock，0x30表示3个clock，其实这两个都可以

